package com.pattern.parallel.completableFuture;

import java.util.concurrent.*;

public class CompletableFutureDemo {

    public static void main(String[] args) throws Exception {
        // 创建一个带result的CompletableFuture
        CompletableFuture<String> future = CompletableFuture.completedFuture("hello world");
        System.out.println(future.get());

        // 默认创建的CompletableFuture是没有result的，这时调用future.get()会一直阻塞下去知道有result或者出现异常
        future = new CompletableFuture<>();
        try {
            future.get(1, TimeUnit.SECONDS);
        } catch (Exception e) {
            // no care
        }
        // 给future填充一个result
        future.complete("result");
        assert "result".equals(future.get());
        // 给future填充一个异常
        future = new CompletableFuture<>();
        future.completeExceptionally(new RuntimeException("exception"));
        try {
            future.get();
        } catch (Exception e) {
            assert "exception".equals(e.getCause().getMessage());
        }

        // runAsync是Runnable任务，不带返回值的，如果入参有executor，则使用executor来执行异步任务
        // public static CompletableFuture<Void>  runAsync(Runnable runnable)
        // public static CompletableFuture<Void>  runAsync(Runnable runnable, Executor executor)
        // supplyAsync是待返回结果的异步任务
        // public static <U> CompletableFuture<U>  supplyAsync(Supplier<U> supplier)
        // public static <U> CompletableFuture<U>  supplyAsync(Supplier<U> supplier, Executor executor)
        ExecutorService executor = new ThreadPoolExecutor(0,
                Runtime.getRuntime().availableProcessors() * 5, 120, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
        CompletableFuture.runAsync(() -> {
            System.out.println("hello world 2");
        }, executor);
        CompletableFuture.supplyAsync(() -> {
            System.out.println("hello world 3");
            return "result";
        });
        //如果入参不带executor，则默认使用ForkJoinPool.commonPool()作为执行异步任务的线程池；否则使用executor执行任务。

        //CompletableFuture的完成动作
        CompletableFuture.supplyAsync(() -> {
            return "result";
        }).whenComplete((result, e) -> {
            System.out.println(result + " " + e);
        }).exceptionally((e) -> {
            System.out.println("exception " + e);
            return "exception";
        });

        // handle方法，可以返回一个新的CompletableFuture的返回类型。
        // handle方法示例：
        CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("hello world 4");
            return "result";
        });
        CompletableFuture<Integer> f2 = f1.handle((r, e) -> {
            System.out.println("handle");
            return 1;
        });
        // 除了使用handle方法来执行CompletableFuture返回类型转换之外，还可以使用thenApply方法，
        // 二者不同的是前者会处理正常返回值和异常，因此可以屏蔽异常，避免继续抛出；而后者只能处理正常返回值，一旦有异常就会抛出。
        // thenApply方法示例：
        CompletableFuture.supplyAsync(() -> {
            System.out.println("hello world 5");
            return "result";
        }).thenApply((r) -> {
            System.out.println(r);
            return "aaa";
        }).thenApply((r) -> {
            System.out.println(r);
            return 1;
        });
        // 注意，上面的handle、thenApply都是返回新的CompletableFuture类型，
        // 如果只是为了在CompletableFuture完成之后执行某些消费动作，而不返回新的CompletableFuture类型，则可以使用thenAccept方法。
        // thenAccept方法示例：
        CompletableFuture.supplyAsync(() -> {
            System.out.println("hello world 6");
            return "result";
        }).thenAccept(r -> {
            System.out.println(r);
        }).thenAccept(r -> {
            // 这里的r为Void（null）了
            System.out.println(r);
        });

        // 上面的handle、thenApply和thenAppept都是对上一个CompletableFuture执行完的结果进行某些操作。
        // 那么可不可以同时对2个CompletableFuture执行结果执行某些操作呢？其实也是可以的，使用thenAppeptBoth方法即可。
        // 注意，thenAppeptBoth和handle/thenApply/thenAppep的流程是一样的，
        // 只不过thenAppeptBoth中包含了另一个CompletableFuture对象
        // （注意，这里另一个CompletableFuture对象的执行可并不是上一个CompletableFuture执行结束才开始执行的）。
        // thenAcceptBoth方法示例：
        CompletableFuture.supplyAsync(() -> {
            System.out.println("hello world 7");
            return "result";
        }).thenAcceptBoth(CompletableFuture.completedFuture("result2"), (r1, r2) -> {
            System.out.println(r1 + "-" + r2);
        });
        // 注意，thenAcceptBoth方法是没有返回值的（CompletableFuture<Void>），
        // 如果想用thenAcceptBoth这样的功能并且还带有返回值的CompletableFuture，那么thenCombine方法就该上场了。
        // thenCombine方法示例
        CompletableFuture.supplyAsync(() -> {
            System.out.println("hello world 8");
            return "result";
        }).thenCombine(CompletableFuture.completedFuture("result2"), (r1, r2) -> {
            System.out.println(r1 + "-" + r2);
            return r1 + "-" + r2;
        });
        // thenAcceptBoth和runAfterBoth是当两个CompletableFuture都计算完成，
        // 而下面的方法是当任意一个CompletableFuture计算完成的时候就会执行。
        // acceptEither

        // 如果当想在多个CompletableFuture都计算完成或者多个CompletableFuture中的一个计算完成后执行某个动作，
        // 可使用方法 allOf 和 anyOf。

        // 如果当任务完成时并不想用CompletableFuture的结果，可以使用thenRun方法来执行一个Runnable。

        // 以上方法都是在方法中返回一个值（或者不返回值），
        // 其实还可以返回一个CompletableFuture，是不是很像类的组合一样。
        // thenCompose方法示例：
        CompletableFuture.supplyAsync(() -> {
            System.out.println("hello world 9");
            return "result";
        }).thenCompose(r -> {
            System.out.println(r);
            return CompletableFuture.supplyAsync(() -> {
                System.out.println(r + " result2");
                return r + " result2";
            });
        });
        // 上面的代码和下面的代码效果是一样的
        CompletableFuture.supplyAsync(() -> {
            System.out.println("hello world 10");
            return "result";
        }).thenApply(r -> {
            System.out.println(r);
            return r;
        }).thenApplyAsync(r -> {
            System.out.println(r + " result2");
            return r + " result2";
        });
    }
}